package com.ybear.ybutils.utils

import android.content.Context
import android.graphics.Bitmap
import android.graphics.Bitmap.CompressFormat
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import android.text.TextUtils
import androidx.annotation.UiThread
import androidx.collection.ArraySet
import com.ybear.ybutils.utils.SysUtil.refreshSystemPicture
import com.ybear.ybutils.utils.handler.HandlerManage
import com.ybear.ybutils.utils.handler.ThreadPool
import com.ybear.ybutils.utils.time.DateTime
import java.io.ByteArrayOutputStream
import java.io.File
import java.io.FileOutputStream
import java.io.FileWriter
import java.io.IOException
import java.nio.charset.Charset
import java.util.Locale
import java.util.concurrent.ConcurrentLinkedQueue

/**
 * 存储工具类
 */
object IOUtil {
    @JvmStatic
    private val deleteFilePathQueue = ConcurrentLinkedQueue<String>()

    /**
     * 保存sp
     * @param context       上下文
     * @param cdName        名称
     * @param data          数据
     * @return              结果
     */
    @JvmStatic
    fun writeSharedPreferences(context: Context, cdName: String, data: Map<String?, Any?>): Boolean {
        val sp = context.getSharedPreferences(cdName, Context.MODE_PRIVATE)
        val editor = sp.edit()
        for ( key in data.keys ) {
            val value = data[ key ] ?: continue
            when( value ) {
                is Boolean -> editor.putBoolean( key, value.toString().toBoolean() )
                is Float -> editor.putFloat( key, value.toString().toFloat() )
                is Long -> editor.putLong( key, value.toString().toLong() )
                is Int -> editor.putInt( key, value.toString().toInt() )
                is Set<*> -> {
                    val set: MutableSet<String> = ArraySet()
                    val iterator = value.iterator()
                    if ( iterator.hasNext() ) {
                        iterator.next()?.let { set.add( it.toString() ) }
                    }
                    editor.putStringSet( key, set )
                }
                else -> editor.putString( key, value.toString() )
            }
        }
        val isCommit: Boolean = editor.commit()
        editor.clear()
        return isCommit
    }

    /**
     * 获取sp
     * @param context       上下文
     * @param cdName        名称
     * @return              返回数据
     */
    @JvmStatic
    fun readSharedPreferences(context: Context, cdName: String): Map<String, Any?> {
        return HashMap( context.getSharedPreferences( cdName, Context.MODE_PRIVATE ).all )
    }

    /**
     * 删除sp
     * @param context       上下文
     * @param cdName        名称
     * @return              结果
     */
    @JvmStatic
    fun deleteSharedPreferences(context: Context, cdName: String): Boolean {
        val sp = context.getSharedPreferences( cdName, Context.MODE_PRIVATE )
        return sp != null && sp.edit().clear().commit()
    }

    /**
     * 删除sp
     * @param context       上下文
     * @param cdName        名称
     * @param keys          删除的key
     * @return              结果
     */
    @JvmStatic
    fun deleteSharedPreferences(context: Context, cdName: String, vararg keys: String): Boolean {
        val sp = context.getSharedPreferences(
            cdName, Context.MODE_PRIVATE
        ) ?: return false
        val editor = sp.edit()
        for (k in keys) editor.remove(k)
        return editor.commit()
    }

    /**
     * 保存图片到相册
     * @param context       上下文
     * @param title         标题
     * @param description   描述
     * @param data          图片
     * @return              保存成功返回路径，否则返回`null`
     */
    @Deprecated( "insertImage", ReplaceWith(
        "MediaStore.Images.Media.insertImage(context.contentResolver, data, title, description)",
        "android.provider.MediaStore"
    ) )
    @UiThread
    @JvmStatic
    fun writeImageToDCIM(context: Context, title: String, description: String, data: Bitmap): String {
        return MediaStore.Images.Media.insertImage(
            context.contentResolver, data, title, description
        )
    }

    /**
     * 删除文件
     * 该方法以线程的形式完成，多次调用仅运行一个线程，删除方式为队列
     * @param context       上下文
     * @param onResult      处理结果。invoke( index, result )
     * @param paths         路径
     */
    @JvmStatic
    fun deleteFile(context: Context, onResult: ((Int, Int) -> Unit)?, vararg paths: String) {
        if ( paths.isEmpty() ) return
        val count = deleteFilePathQueue.size
        deleteFilePathQueue.addAll( paths )
        if( count > 0 ) return
        ThreadPool.get().execute {
            var index = 0
            var result = 0
            while ( deleteFilePathQueue.isNotEmpty() ) {
                try {
                    val firstPath = deleteFilePathQueue.poll() ?: continue
                    // 删除缩略图文件
                    val file = File( firstPath )
                    if ( file.exists() && file.delete() ) {
                        // 删除残余文件
                        context.contentResolver.delete(
                            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
                            MediaStore.Images.Media.DATA + "=?",
                            arrayOf( firstPath )
                        )
                        result = 1
                    }
                } catch (e: Exception) {
                    e.printStackTrace()
                    result = -1
                } finally {
                    onResult?.invoke( index++, result )
                }
            }
            onResult?.invoke( -1, -1 )
        }
    }

    @JvmStatic
    fun deleteFile(context: Context, vararg paths: String?) {
        deleteFile(context, null, *paths)
    }

    /**
     * 获取文件
     * @param path          路径
     * @param fullName      全称 file_name.txt
     */
    @JvmStatic
    fun getFile(path: String, fullName: String?): File {
        return if (fullName != null) {
            val separator = File.separator
            // 斜杠判断
            val fullPath = if ( !path.endsWith( separator ) && !fullName.startsWith( separator ) ) {
                "$path$separator$fullName"
            } else {
                "$path$fullName"
            }
            File( fullPath )
        } else {
            File( path )
        }
    }

    /**
     * 保存文件
     * @param path          路径
     * @param fullName      全称 file_name.txt
     * @param data          数据
     * @param append        是否追加
     * @return              结果
     */
    @JvmOverloads
    @JvmStatic
    fun saveFile(
        path: String?,
        fullName: String?,
        data: ByteArray,
        append: Boolean,
        onResult: ((Long) -> Unit)? = null
    ) {
        var p = path
        if ( p.isNullOrEmpty() ) {
            onResult?.invoke( -1 )
            return
        }
        val handler = HandlerManage.create()
        handler.postAsync({ call ->
            var retLen: Long = -1
            val f: File
            if ( fullName != null ) {
                val s7r = File.separator
                //斜杠判断
                if ( !p.endsWith( s7r ) && !fullName.startsWith( s7r ) ) p += s7r
                f = File( p + fullName )
            } else {
                f = File( p )
            }
            var w: FileWriter? = null
            //文件是否存在，创建目录
            if ( !f.exists() && File( p ).mkdirs() ) {
                try {
                    //创建文件
                    f.createNewFile()
                } catch (e: IOException) {
                    e.printStackTrace()
                    call.accept( retLen )
                    return@postAsync
                }
            }
            try {
                w = FileWriter( f, append )
                for ( b in data ) w.write( b.toInt() )
            } catch (e: IOException) {
                e.printStackTrace()
            } finally {
                retLen = f.length()
                call.accept( retLen )
                w?.let {
                    try { it.flush() } catch (e: IOException) { e.printStackTrace() }
                    try { it.close() } catch (e: IOException) { e.printStackTrace() }
                }
            }
        }) {
            onResult?.invoke( it )
        }
    }

    /**
     * 保存文件
     * @param path          路径
     * @param fullName      全称 file_name.txt
     * @param data          数据
     * @return              结果
     */
    @JvmOverloads
    @JvmStatic
    fun saveFile(
        path: String?, fullName: String?, data: ByteArray, onResult: ((Long) -> Unit)? = null
    ) {
        saveFile( path, fullName, data, false, onResult )
    }

    /**
     * 保存文件
     * @param fullPath      完整路径
     * @param data          数据
     * @param append        是否追加
     * @return              结果
     */
    @JvmOverloads
    @JvmStatic
    fun saveFile(
        fullPath: String?, data: ByteArray, append: Boolean, onResult: ((Long) -> Unit)? = null
    ) {
        saveFile( fullPath, null, data, append, onResult )
    }

    /**
     * 保存文件
     * @param fullPath      完整路径
     * @param data          数据
     * @return              结果
     */
    @JvmOverloads
    @JvmStatic
    fun saveFile(
        fullPath: String?,
        data: ByteArray,
        onResult: ((Long) -> Unit)? = null
    ) {
        saveFile( fullPath, null, data, false, onResult )
    }

    /**
     * 保存日志文件
     * @param context       上下文
     * @param name          日志名
     * @param data          数据
     * @return              结果
     */
    @JvmOverloads
    @JvmStatic
    fun saveLogFile(
        context: Context,
        name: String?,
        data: String,
        append: Boolean,
        charset: Charset?,
        onResult: ((Boolean) -> Unit)? = null
    ) {
        val f = context.getExternalFilesDir(null)
        val sc = File.separatorChar
        //设置保存文件路径
        val path = if ( f != null ) f.absolutePath + sc + "logs" + sc else null
        //保存文件
        saveFile(
            path, name, data.toByteArray( charset ?: Charset.defaultCharset() ), append
        ) {
            onResult?.invoke( it != -1L )
        }
    }

    /**
     * 保存日志文件
     * @param context       上下文
     * @param data          数据
     * @return              结果
     */
    @JvmOverloads
    @JvmStatic
    fun saveLogFile(
        context: Context,
        data: String,
        append: Boolean = false,
        onResult: ((Boolean) -> Unit)? = null
    ) {
        val millis = System.currentTimeMillis()
        saveLogFile(
            context, String.format("%s_%s.log", DateTime.parse(millis), millis),
            data,
            append,
            Charset.defaultCharset(),
            onResult
        )
    }

    @JvmStatic
    fun createPicturesFile(context: Context, fileName: String?): File? {
        context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
        val f: File? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
        } else {
            Environment.getExternalStorageDirectory()
        }
        return fileName?.let { File(f, it) }
    }

    /**
     * 保存Bitmap
     * @param context       上下文
     * @param bitmap        图片
     * @param file          保存路径
     * @return              保存结果
     */
    @JvmOverloads
    @JvmStatic
    fun saveBitmap(context: Context?, bitmap: Bitmap?, file: File?, quality: Int = 100): Boolean {
        if (context == null || bitmap == null || file == null) return false
        try {
            FileOutputStream(file).use { fos ->
                val path = file.absolutePath
                val suffix = path.substring(path.lastIndexOf(".") + 1)
                var format = CompressFormat.JPEG
                if (!TextUtils.isEmpty(suffix)) {
                    when (suffix.lowercase(Locale.getDefault())) {
                        "png" -> format = CompressFormat.PNG
                        "webp" -> {
                            format = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
                                //quality 为 100时，默认无损压缩
                                if( quality == 100 )
                                    CompressFormat.WEBP_LOSSLESS
                                else
                                    CompressFormat.WEBP_LOSSY
                            else
                                CompressFormat.WEBP

                        }
                    }
                }
                bitmap.compress( format, quality, fos )
                fos.flush()
                //TODO 这里不用刷新
//            //通知系统图库更新
//            SysUtil.refreshSystemPicture( context, file );
                return true
            }
        } catch (e: IOException) {
            e.printStackTrace()
            return false
        }
    }

    /**
     * 保存Bitmap
     * @param context       上下文
     * @param bitmap        图片
     * @param fileName      文件名
     * @return              保存结果
     */
    @JvmOverloads
    @JvmStatic
    fun saveBitmapToPicture(context: Context, bitmap: Bitmap?, fileName: String?, quality: Int = 100
    ): File? {
        val file = createPicturesFile(context, fileName)
        val result = saveBitmap(context, bitmap, file, quality)
        refreshSystemPicture(context, file)
        return if (result) file else null
    }

    /**
     * Bitmap 转 ByteArray
     */
    @JvmOverloads
    @JvmStatic
    fun bitmapToByteArray(bitmap: Bitmap,
                          format: CompressFormat = CompressFormat.PNG,
                          quality: Int = 100) : ByteArray {
        val out = ByteArrayOutputStream()
        bitmap.compress( format, quality, out )
        return out.toByteArray()
    }

}